//--------------------------------------------------
/**
 *
 *      Hand Gesture Recognition System
 *
 */
//--------------------------------------------------
/**
 * //CORE CLASS//
 * Librería Auxiliar de procesos
 *
 * @author  Edwin Cobos & Cesar Murcia
 * @version Core_v1.3
 *
 * Revisado: 12/11/11
 */
//--------------------------------------------------
//PACKAGE//
package HGR;
//IMPORT//
import OpenCV.cv;
import OpenCV.cv.IplImage;
import com.sun.jna.Pointer;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import javax.imageio.ImageIO;

//--------------------------------------------------
/**
 *Core Class Realiza los procesos secundarios del sistema
 */
public abstract class Core
{
    //--------------------------------------------------
    //KERNEL CONVOLUCIÓN    
    /**
     * Kernel estándar del filtro Pasabajos
     */
    public static float[] lp_kernel =
    {1/9f, 1/9f, 1/9f,
     1/9f, 1/9f, 1/9f,
     1/9f, 1/9f, 1/9f};

    /**
     * Kernel de tipo diamante 3x3
     */
    public static float[] kernel3_Diamond0 = 
    {  0.0f, 255.0f,   0.0f,
     255.0f ,  0.0f, 255.0f,
       0.0f, 255.0f,   0.0f};

    /**
     * Kernel de tipo cruz 3x3
     */
    public static float[] kernel3_Cross =    
    {  0.0f, 255.0f,   0.0f,
     255.0f, 255.0f, 255.0f,
       0.0f, 255.0f,   0.0f};

    /**
     * Kernel de tipo H 3x3
     */
    public static float[] kernel3_H =    
    {255.0f,   0.0f, 255.0f,
     255.0f, 255.0f, 255.0f,
     255.0f,   0.0f, 255.0f};

   /**
    * Kernel de tipo Diamante 5x5
    */
    public static float[] kernel5_Diamond0 = 
    {  0.0f,   0.0f, 255.0f,   0.0f,   0.0f,
       0.0f, 255.0f,   0.0f, 255.0f,   0.0f,
     255.0f,   0.0f,   0.0f,   0.0f, 255.0f,
       0.0f, 255.0f,   0.0f, 255.0f,   0.0f,
       0.0f,   0.0f, 255.0f,   0.0f,   0.0f};

    /**
     * Kernel de tipo Diamante relleno 5x5
     */
    public static float[] kernel5_Diamond =
    {  0.0f,   0.0f, 255.0f,   0.0f,   0.0f,
       0.0f, 255.0f, 255.0f, 255.0f,   0.0f,
     255.0f, 255.0f, 255.0f, 255.0f, 255.0f,
       0.0f, 255.0f, 255.0f, 255.0f,   0.0f,
       0.0f,   0.0f, 255.0f,   0.0f,   0.0f};

    /**
     * Kernel de tipo Circulo 5x5
     */
    public static float[] kernel5_Circle =
    {  0.0f,   0.0f, 255.0f,   0.0f,   0.0f,
       0.0f, 255.0f,   0.0f, 255.0f,   0.0f,
     255.0f,   0.0f,   0.0f,   0.0f, 255.0f,
       0.0f, 255.0f,   0.0f, 255.0f,   0.0f,
       0.0f,   0.0f, 255.0f,   0.0f,   0.0f};

    /**
     * Kernel de tipo Cuadrado 5x5
     */
    public static float[] kernel5_Square =
    {0.0f,   0.0f,   0.0f,   0.0f, 0.0f,
     0.0f, 255.0f, 255.0f, 255.0f, 0.0f,
     0.0f, 255.0f, 255.0f, 255.0f, 0.0f,
     0.0f, 255.0f, 255.0f, 255.0f, 0.0f,
     0.0f,   0.0f,   0.0f,   0.0f, 0.0f};

    /**
     * Kernel de tipo Circulo relleno 7x7
     */
    public static float[] kernel7_Circle =
    {  0.0f,   0.0f,   0.0f, 255.0f,   0.0f,   0.0f,   0.0f,
       0.0f, 255.0f, 255.0f, 255.0f, 255.0f, 255.0f,   0.0f,
       0.0f, 255.0f, 255.0f, 255.0f, 255.0f, 255.0f,   0.0f,
     255.0f, 255.0f, 255.0f, 255.0f, 255.0f, 255.0f, 255.0f,
       0.0f, 255.0f, 255.0f, 255.0f, 255.0f, 255.0f,   0.0f,
       0.0f, 255.0f, 255.0f, 255.0f, 255.0f, 255.0f,   0.0f,
       0.0f,   0.0f,   0.0f, 255.0f,   0.0f,   0.0f,   0.0f};

    /**
     * Kernel de tipo Diamante relleno 7x7
     */
    public static float[] kernel7_Diamond = 
    {  0.0f,   0.0f,   0.0f, 255.0f,   0.0f,   0.0f,   0.0f,
       0.0f,   0.0f, 255.0f, 255.0f, 255.0f,   0.0f,   0.0f,
       0.0f, 255.0f, 255.0f, 255.0f, 255.0f, 255.0f,   0.0f,
     255.0f, 255.0f, 255.0f, 255.0f, 255.0f, 255.0f, 255.0f,
       0.0f, 255.0f, 255.0f, 255.0f, 255.0f, 255.0f,   0.0f,
       0.0f,   0.0f, 255.0f, 255.0f, 255.0f,   0.0f,   0.0f,
       0.0f,   0.0f,   0.0f, 255.0f,   0.0f,   0.0f,   0.0f};
    
    //------------------------------------------
    /**
     * A partir de la imagen almacenada en un <b>Pointer</b> crea un archivo <b>"*.jpg"</b> en la carpeta de trabajo.
     * @param im Puntero de la imagen a almacenar
     */
    public static void writeImage(Pointer im, int type)
    {
        BufferedImage img = image2BufferedImage(im, 3, type);
        String filename = "captured";
        filename = filename.concat(".jpg");
        File file = new File(filename);
        try
        {
            ImageIO.write(img,"jpg",file);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    public static void FinCamera()
    {
        
        Pointer out = new Pointer(0);        

       
       
        //cv.releaseCapture(RTData.getCamDevice().getPointer(0));
        //cv.releaseCapture(RTData.getCamDevice());
        //camDev = null;
    }

    //--------------------------------------------------
    /**
     * Convierte una imagen de tipo <b>Pointer</b> a <b>BufferedImage</b>
     * @param ptrImg Imagen a convertir
     * @param channels Número de canales de la imagen
     * @param type Conversión de color a realizar
     * @return
     */
    public static BufferedImage image2BufferedImage(Pointer ptrImg, int channels, int type)
    {
        IplImage cvImg = new IplImage(ptrImg);
        IplImage cvConverted = new IplImage(cv.createImage(cvImg.width,cvImg.height,cvImg.depth,channels));
        cv.cvtColor(cvImg.getPointer(),cvConverted.getPointer(),type);
        Pointer dst = new Pointer(cvConverted.imageData);
        int[] buffer = dst.getIntArray(0,cvImg.width*cvImg.height);
        BufferedImage bImage = new BufferedImage(cvImg.width,cvImg.height,BufferedImage.TYPE_INT_RGB);
        bImage.setRGB(0,0,cvImg.width,cvImg.height,buffer,0,cvImg.width);
        return bImage;
    }

    //--------------------------------------------------
    /**
     * Convierte una imagen de <b>BufferedImage</b> a <b>Pointer</b>
     * @param img Imagen a convertir
     * @param channels Número de canales de la imagen
     * @return
     */
    public static Pointer BufferedImage2Pointer(BufferedImage img, int channels)
    {
        Pointer ptrImg = cv.createImage(img.getWidth(), img.getHeight(), 8, channels);
        int[] buf = new int[img.getWidth() * img.getHeight()];
        img.getRGB(0, 0, img.getWidth(), img.getHeight(), buf, 0, img.getWidth());
        cv.IplImage cvImg = new cv.IplImage(ptrImg);
        Pointer dst = new Pointer(cvImg.imageData);
        dst.write(0,buf,0,buf.length);
        return ptrImg;
    }

    //--------------------------------------------------
    /**
     * Escribe un <b>byte buffer</b> a una imagen de tipo <b>Pointer</b>
     * @param ptrImg Puntero de la imagen para escribir
     * @param buffer Buffer de la imagen a escribir
     */
    public static void writeBuffer(Pointer ptrImg, byte buffer[])
    {
        IplImage cvImg = new IplImage(ptrImg);
        Pointer dst = new Pointer(cvImg.imageData);
        dst.write(0,buffer,0,buffer.length);
    }        

    //--------------------------------------------------
    /**
     * Captura el frame actual, lo convierte a escala de grises obteniendo el buffer de trabajo en <b>float[] buffer</b>
     * @param camDev Pointer del dispositivo
     * @param image_ptr Variable para obtener los datos del frame actual
     * @param image_gray Pointer en el cual se va a almacenar el frame en escala de grises
     * @param buffer Array en el cual se va a almacenar los datos del frame
     */
    public static float[] CaptureFrame(Pointer camDev, Pointer image_gray, int length)
    {
        //Pointer image_gray = cv.createImage(RTData.getWidth(), RTData.getHeight(), 8, 1);
        Pointer f = cv.createImage(HGR_gui.inWidth, HGR_gui.inHeight, 8, 3);
        cv.Resize(cv.queryFrame(camDev), f, 0);
        cv.cvtColor(f, image_gray, cv.CV_RGB2GRAY);
        byte[] bufferByte = Pointer2Byte(image_gray);
        float[] buffer = new float[length];
        int k;

        for(k=0; k<buffer.length; k++)
        {
            if(bufferByte[k]<0)
                buffer[k] = bufferByte[k] & 255;
            else
                buffer[k] = bufferByte[k];
        }

        return buffer;
    }

    //--------------------------------------------------
    /**
     * Captura el frame actual, lo convierte a escala de grises obteniendo el buffer de trabajo en <b>float[] buffer</b>
     * @param camDev Pointer del dispositivo
     * @param image_ptr Variable para obtener los datos del frame actual
     * @param image_gray Pointer en el cual se va a almacenar el frame en escala de grises
     * @param buffer Array en el cual se va a almacenar los datos del frame
     */
    public static void StartCaptureLearning(Pointer camDev, IplImage image_ptr, Pointer image_gray, float[] buffer)
    {
        image_ptr = new IplImage(cv.queryFrame(camDev));
        cv.cvtColor(image_ptr.getPointer(), image_gray, cv.CV_BGR2GRAY);
        byte[] bufferByte = Pointer2Byte(image_gray);
        int k;
        for(k=0; k<buffer.length; k++)
        {
            if(bufferByte[k]<0)
                buffer[k] = bufferByte[k] & 255;
            else
                buffer[k] = bufferByte[k];
        }
    }

    public static Pointer StartCamera()
    {        
        Pointer camDev = cv.createCameraCapture(0);
        if(camDev==null)
        {
            System.out.println("No camera conected");            
        }
        else
        {
            System.out.println("Camera conected");            
        }
        return camDev;
    }

    //--------------------------------------------------
    /**
     * Realiza la diferencia entre <b>buffer[]</b> y <b>bufferBg[]</b>
     * @param buffer Imagen de trabajo (Sustractor)
     * @param bufferBg Imagen de fondo (Sustraendo)
     * @param bufferBgS Imagen resultante (Destino)
     * @param avgBG Promedio de la imagen de fondo
     * @param varBG Mediana de la imagen de fondo
     * @param Threshold Umbral para la binarización <b>(0-1)</b>
     */
    public static float[] BackgroundSubtraction(float[] bufferOri, float[] bufferBg, float avgBG, float varBG, float Threshold)
    {
        float[] bufferBgS = new float[bufferOri.length];
        float[] buffer = new float[bufferOri.length];        
        System.arraycopy(bufferOri, 0, buffer, 0, bufferOri.length);
        float avgWork=0.0f;
        float varWork=0.0f;
        avgWork = avgImage(buffer);
        varWork = varImage(buffer, avgWork);
        float Max = 0.0f;
        float Min = 255.0f;
        int L = buffer.length;
        int i;
        for(i=0;i<L;i++)
        {
            buffer[i] = (int)(buffer[i] - avgWork);
            buffer[i] = (int)(buffer[i] * ( varWork / varBG ));
            buffer[i] = (int)(buffer[i] + avgBG);
            bufferBgS[i] = Math.abs(bufferBg[i] - buffer[i]); // Diferencia
            if(bufferBgS[i] > Max)
                Max = bufferBgS[i];
            if(bufferBgS[i] < Min)
                Min = bufferBgS[i];
        }

        float auxBuf = 0;        
        for(i=0;i<L;i++)
        {
            auxBuf = ((bufferBgS[i] - Min) / (Max - Min));
            if(auxBuf > Threshold)
                bufferBgS[i] = 255;
            else
                bufferBgS[i] = 0;
        }

        return bufferBgS;
    }

    public static void BackgroundSubtractionLearning(float[] buffer, float[] bufferBg, float[] bufferBgS, float avgBG, float varBG, float Threshold)
    {
        float avgWork=0.0f;
        float varWork=0.0f;
        avgWork = avgImage(buffer);
        varWork = varImage(buffer, avgWork);
        float Max = 0.0f;
        float Min = 255.0f;
        int L = buffer.length;
        int i;
        for(i=0;i<L;i++)
        {
            buffer[i] = (int)(buffer[i] - avgWork);
            buffer[i] = (int)(buffer[i] * ( varWork / varBG ));
            buffer[i] = (int)(buffer[i] + avgBG);
            bufferBgS[i] = Math.abs(bufferBg[i] - buffer[i]); // Diferencia
            if(bufferBgS[i] > Max)
                Max = bufferBgS[i];
            if(bufferBgS[i] < Min)
                Min = bufferBgS[i];
        }

        float auxBuf = 0;
        for(i=0;i<L;i++)
        {
            auxBuf = ((bufferBgS[i] - Min) / (Max - Min));
            if(auxBuf > Threshold)
                bufferBgS[i] = 255;
            else
                bufferBgS[i] = 0;
        }
    }

    //--------------------------------------------------
    /**
     * Convierte un <b>Pointer</b> en <b>byte[]</b>
     * @param ptrImg Pointer para convertir
     * @return Array de <b>byte</b>
     */
    public static byte[] Pointer2Byte(Pointer ptrImg)
    {
        IplImage cvImg = new IplImage(ptrImg);
        Pointer dst = new Pointer(cvImg.imageData);
        byte[] buffer = dst.getByteArray(0,cvImg.width*cvImg.height);
        return buffer;
    }

    //--------------------------------------------------
    /**
     * Convierte un arreglo de <b>byte</b> a <b>Pointer</b>
     * @param ptrImg Pointer de destino
     * @param buffer Array para convertir
     */
    public static void byte2Pointer(Pointer ptrImg, byte buffer[])
    {
        IplImage cvImg = new IplImage(ptrImg);
        Pointer dst = new Pointer(cvImg.imageData);
        dst.write(0,buffer,0,buffer.length);
    }

    //--------------------------------------------------
    /**
     * Filtro Mediana
     * @param src Arreglo de la imagen
     * @param aperture Tamaño de la apertura Ej: <b>3</b> = (3x3), <b>5</b> = (5x5)
     * @param W Ancho de la imagen
     * @param H Alto de la imagen
     * @return Imagen filtrada
     */
    public static float[] MedianFilter(float[] src, int aperture, int W, int H)
    {
        int L = W*H;
        float RGBtemp[] = new float[aperture*aperture];
        int middleAp = (int)(aperture/2);
        float[] dst = new float[src.length];
        int ind0=0, ind1=0;
        int i=middleAp, j=middleAp;
        int ix = 0, iy = 0;
        int limite = (L)-(((middleAp*2*W))+((middleAp*2*H)));
        int a, k;
        for(a=0; a<limite; a++)
        {
            for(k=0; k<RGBtemp.length; k++)
            {
                ix = (i-middleAp)+ind0;
                iy = (j-middleAp)+ind1;
                RGBtemp[k] = (src[((W * (iy)) + ix)]);
                ind0++;
                if(ind0 == aperture)
                {
                    ind0=0;
                    ind1++;
                    if(ind1 == aperture)
                        ind1=0;
                }
            }
            Arrays.sort(RGBtemp);
            dst[((W * (j)) + i)] = RGBtemp[(int)(RGBtemp.length/2)];
            i++;
            if(i > W-middleAp)
            {
                i=middleAp;
                j++;
                if(j > H-middleAp)
                    break;
            }
        }
        return dst;
    }

    //--------------------------------------------------
    /**
     * Filtro Pasa-Bajos
     * @param src Arreglo de la imagen
     * @param _kernel Kernel del filtro
     * @param W Ancho de la imagen
     * @param H Alto de la imagen
     * @return Imagen filtrada
     */
    public static float[] LowPassFilter(float[] src, float[] _kernel, int W, int H)
    {
        int L = W*H;
        int aperture = (int)Math.sqrt(_kernel.length);
        float RGBtemp = 0;
        int middleAp = (int)(aperture/2);
        float[] dst = new float[src.length];
        Arrays.fill(dst, 0.0f);
        int ind0=0, ind1=0;
        int i=middleAp, j=middleAp;
        int ix = 0, iy = 0;
        int limite = (L)-(((middleAp*2*W))+((middleAp*2*(H-(2*middleAp)))));
        int a,k;
        float value;
        for(a=0; a<limite; a++)
        {
            RGBtemp = 0;
            for(k=0; k<_kernel.length; k++)
            {
                ix = (i-middleAp)+ind0;
                iy = (j-middleAp)+ind1;
                value = (src[(((W) * (iy)) + ix)] * _kernel[k] );
                RGBtemp += value;
                ind0++;
                if(ind0 == aperture)
                {
                    ind0=0;
                    ind1++;
                    if(ind1 == aperture)
                        ind1=0;
                }
            }

            dst[((W * j) + i)] = (int)RGBtemp;
            i++;
            if(i > (W)-(middleAp))
            {
                i=middleAp;
                j++;
                if(j > (H)-middleAp)
                    break;
            }
        }
        return dst;
    }

    //--------------------------------------------------
    /**
     * Calcula la imagen de Fondo para el Background Subtraction promediando un
     * número de frames determinado
     * @param _kernel Kernel del filtro Pasa-Bajos
     * @param camDev Puntero de la cámara
     * @param buffer Buffer de la imagen actual
     * @param frames Numero de frames para promediar
     * @param image_ptr Imagen del frame actual
     * @param image_gray Imagen del frame actual en grises
     * @param L Longitud de la imagen <b>(W*H)</b>
     * @param W Ancho de la imagen
     * @param H Alto de la Imagen
     * @return Imagen de fondo para el Background Subtraction
     */
    public static void MakeBufferBg(float[] buf, float[] bufProc)
    {
       /*
        int k;
        if(RTData.getFlagBg() < 10)
        {
            if(RTData.getFlagBg() == 0)
            {
                bufProc = RTData.getBufferProcess().getBuffer();
                bufProc = Core.LowPassFilter(bufProc, Core.lp_kernel, RTData.getWidth(), RTData.getHeight());
                bufProc = Core.MedianFilter(bufProc, 3, RTData.getWidth(), RTData.getHeight());
                RTData.setBufferBg(bufProc);
            }else
            {
                bufProc = RTData.getBufferProcess().getBuffer();
                bufProc = Core.LowPassFilter(bufProc, Core.lp_kernel, RTData.getWidth(), RTData.getHeight());
                bufProc = Core.MedianFilter(bufProc, 3, RTData.getWidth(), RTData.getHeight());
                for(k=0; k<RTData.getBufferProcess().getBuffer().length; k++)
                {
                    //_buffer[k] += buffer[k];
                    buf[k] = (bufProc[k] + RTData.getBufferBg()[k])/2.0f;
                }
                RTData.setBufferBg(buf);
            }

            if(RTData.getFlagBg() == 9)
            {
                for(k=0; k<RTData.getBufferProcess().getBuffer().length; k++)
                {
                    RTData.setBufferBgData(k, RTData.getBufferBgData(k)*RTData.getContrastBg());
                }
                RTData.setAvgBG( Core.avgImage(RTData.getBufferBg()) );
                RTData.setVarBG( Core.varImage(RTData.getBufferBg(), RTData.getAvgBG()) );
            }

            RTData.setFlagBg(RTData.getFlagBg()+1);            
        }


        //return buffer;

        */
    }


    public static float[] MakeBufferBgLearning(float[] _kernel, Pointer camDev,
            float[] buffer, int frames, IplImage image_ptr, Pointer image_gray, int L, int W, int H)
    {
        int time = 0;
        float[] _buffer = new float[L];
        int k;
        while(time < frames)
        {
            StartCaptureLearning(camDev, image_ptr, image_gray, buffer);
            //CaptureFrame(camDev, image_ptr, image_gray, buffer);
            buffer = LowPassFilter(buffer, _kernel, W, H);
            buffer = MedianFilter(buffer, 3, W, H);
            for(k=0; k<L; k++)
            {
                _buffer[k] += buffer[k];
            }
            time++;
        }
        int i = 0;
        for(i=0;i<L;i++)
            _buffer[i]/=frames;

        return _buffer;
    }

    

    //--------------------------------------------------
    /**
     * Calcula el promedio de una imagen
     * @param _buffer Imagen a promediar
     * @return Promedio de la imagen
     */
    public static float avgImage(float[] _buffer)
    {
        float avg = 0;
        int i;
        for(i=0;i<_buffer.length;i++)
        {
            avg += _buffer[i];
        }
        avg/=_buffer.length;
        return avg;
    }

    //--------------------------------------------------
    /**
     * Calcula la mediana de una imagen
     * @param _buffer Imagen a promediar
     * @param avg Promedio de la imagen
     * @return Mediana de la imagen
     */
    public static float varImage(float[] _buffer, float avg)
    {
        float var = 0;
        int i;
        for(i=0;i<_buffer.length;i++)
        {
            var += (_buffer[i]-avg)*(_buffer[i]-avg);
        }
        var/=_buffer.length;
        return var;
    }

    //--------------------------------------------------
    /**
     * Filtro de Erosión
     * @param _kernel Kernel del filtro
     * @param src Array de la imagen
     * @param W Ancho de la imagen
     * @param H Alto de la imagen
     * @return Imagen filtrada
     */
    public static float[] ErosionFilter(float[] _kernel, float[] src, int W, int H)
    {
        int L =W*H;
        int aperture = (int)Math.sqrt(_kernel.length);
        int middleAp = (int)(aperture/2);
        float[] bufferTmp = new float[L];
        int limite = (L)-(((middleAp*2*W))+((middleAp*2*(H-(2*middleAp)))));
        int ind0=0, ind1=0;
        int i=middleAp, j=middleAp;
        int ix = 0, iy = 0;
        int a,k;
        boolean flag = false;
        for(a=0; a<limite; a++)
        {
            flag = false;
            for(k=0; k<_kernel.length; k++)
            {
                ix = (i-middleAp)+ind0;
                iy = (j-middleAp)+ind1;
                if(src[((W * (iy)) + ix)] != 255 && _kernel[k] != 255)
                    flag = true;

                ind0++;
                if(ind0 == aperture)
                {
                    ind0=0;
                    ind1++;
                    if(ind1 == aperture)
                        ind1=0;
                }
            }
            bufferTmp[((W * (j)) + i)] = (flag == false ? 255 : 0);

            i++;
            if(i > W-middleAp)
            {
                i=middleAp;
                j++;
                if(j > H-middleAp)
                    break;
            }
        }
        return bufferTmp;

    }

    //--------------------------------------------------
    /**
     * Filtro de Dilatación
     * @param _kernel Kernel del filtro
     * @param src Array de la imagen
     * @param W Ancho de la imagen
     * @param H Alto de la imagen
     * @return Imagen filtrada
     */
    public static float[] DilatationFilter(float[] _kernel, float[] src, int W, int H)
    {
        int L=W*H;
        int aperture = (int)Math.sqrt(_kernel.length);
        int middleAp = (int)(aperture/2);
        float[] bufferTmp = new float[L];
        int limite = (L)-(((middleAp*2*W))+((middleAp*2*(H-(2*middleAp)))));
        int ind0=0, ind1=0;
        int i=middleAp, j=middleAp;
        int ix = 0, iy = 0;
        int a,k;
        boolean flag = false;
        for(a=0; a<limite; a++)
        {
            flag = false;
            for(k=0; k<_kernel.length; k++)
            {
                ix = (i-middleAp)+ind0;
                iy = (j-middleAp)+ind1;
                if(src[((W * (iy)) + ix)] == 255 && _kernel[k] == 255){
                    flag = true;
                }

                ind0++;
                if(ind0 == aperture)
                {
                    ind0=0;
                    ind1++;
                    if(ind1 == aperture)
                        ind1=0;
                }
            }
            bufferTmp[((W * (j)) + i)] = (flag == true ? 255 : 0);

            i++;
            if(i > W-middleAp)
            {
                i=middleAp;
                j++;
                if(j > H-middleAp)
                    break;
            }
        }
        return bufferTmp;

    }

    //--------------------------------------------------
    /**
     * Aplica <b>buffer[]</b> como mascara a <b>bufferOrigin[]</b>
     * @param buffer Array de la mascara
     * @param bufferOrigin Array de la imagen
     * @return Imagen enmascarada
     */
    public static float[] MaskImage(float[] buffer, float[] bufferOrigin)
    {
        int L=buffer.length;
        float[] bufferTmp = new float[L];
        int limite = L;
        int a;
        for(a=0; a<limite; a++)
        {
            if(buffer[a] == 255)
                bufferTmp[a]=bufferOrigin[a];
            else
                bufferTmp[a]=0;
        }
        return bufferTmp;
    }

    //--------------------------------------------------
    /**
     * Etiquetado de regiones utilizando vecindad 4
     * @param dst Array de la imagen de destino para las etiquetas
     * @param src Array de la imagen original
     * @param _W Ancho de la imagen
     * @param _H Alto de la imagen
     * @return Listado de las regiones
     */
    public static float[] LabeledRegions(float[] dst, float[] src, int _W, int _H)
    {
        Arrays.fill(dst, 0.0f);
        int _L = src.length;
        ArrayList ListLabels = new ArrayList();
        ArrayList NameLabels = new ArrayList();
        int middleAp = 1;       
        int limite = (_L)-(((middleAp*2*_W))+((middleAp*2*(_H-(2*middleAp)))));
        int i=middleAp, j=middleAp;
        int a;
        int label = 0;

        for(a=0; a<limite; a++)
        {
            if(src[((_W * (j)) + i)] == 255.0f)
            {
                //Si ninguno, entonces nueva etiqueta
                if(src[((_W * (j-1)) + i)] == 0.0f  &&  src[((_W * (j)) + (i-1))] == 0.0f)
                {
                    label+=1;
                    ListLabels.add(label);
                    dst[((_W * (j)) + i)] = label;

                }
                else if(src[((_W * (j-1)) + i)] == 255.0f && src[((_W * (j)) + (i-1))] == 255.0f && dst[((_W * (j-1)) + i)]!=0 && dst[((_W * (j)) + (i-1))]!=0)
                {
                    if(dst[((_W * (j-1)) + i)] == dst[((_W * (j)) + (i-1))] )
                    {
                        dst[((_W * (j)) + (i))] = Float.valueOf(ListLabels.get(((int)dst[((_W * (j)) + (i-1))])-1).toString()).floatValue();
                    }
                    else
                    {
                        if(dst[((_W * (j-1)) + i)] < dst[((_W * (j)) + (i-1))])
                        {
                            ListLabels.set((int)dst[((_W * (j)) + (i-1))]-1, dst[((_W * (j-1)) + i)]);
                            dst[((_W * (j)) + (i))] = Float.valueOf(ListLabels.get((int)dst[((_W * (j-1)) + i)]-1).toString()).floatValue();
                        }
                        else
                        {                            
                            ListLabels.set((int)dst[((_W * (j-1)) + i)]-1, dst[((_W * (j)) + (i-1))]);
                            dst[((_W * (j)) + (i))] = Float.valueOf(ListLabels.get((int)dst[((_W * (j)) + (i-1))]-1).toString()).floatValue();
                        }
                    }
                }
                else
                {
                    if(src[((_W * (j-1)) + i)] == 255 && dst[((_W * (j-1)) + i)]!=0)
                    {
                       dst[((_W * (j)) + i)] = Float.valueOf(ListLabels.get((int)dst[((_W * (j-1)) + i)]-1).toString()).floatValue();
                    }
                    else if(src[((_W * (j)) + (i-1))] == 255 && dst[((_W * (j)) + (i-1))] != 0)
                    {
                        dst[((_W * (j)) + i)] = Float.valueOf(ListLabels.get((int)dst[((_W * (j)) + (i-1))]-1).toString()).floatValue();
                    }
                }
            }

            i++;
            if(i == _W-middleAp)
            {
                i=middleAp;
                j++;
                if(j == _H-middleAp)
                    break;
            }
        }

        int pos=0;
        label = 0;
        for(a=0; a<ListLabels.size(); a++)
        {
            if(a+1 != ListLabels.get(a))
            {
                pos = (int)(Float.parseFloat(ListLabels.get(a).toString()));
                ListLabels.set(a,ListLabels.get(pos-1));
            }
        }
        //Reasignar etiquetas
        i=middleAp;
        j=middleAp;
        for(a=0; a<limite; a++)
        {
            if(dst[((_W * (j)) + i)] != 0.0f)
            {               
                if(dst[((_W * (j)) + i)] != Float.parseFloat(ListLabels.get((int)dst[((_W * (j)) + i)]-1).toString()))
                {
                   dst[((_W * (j)) + i)] =  Float.parseFloat(ListLabels.get((int)dst[((_W * (j)) + i)]-1).toString());
                }
            }

            i++;
            if(i == _W-middleAp)
            {
                i=middleAp;
                j++;
                if(j == _H-middleAp)
                    break;
            }
        }
        
        Collections.sort(ListLabels); //Organizar el Array
        int act=0;
        for(a=0; a<ListLabels.size(); a++)
        {
            if((int)Float.parseFloat(ListLabels.get(a).toString()) > act)
            {
                act = (int)Float.parseFloat(ListLabels.get(a).toString());
                NameLabels.add(act);
            }
        }
        float[] out = new float[NameLabels.size()*2];   //Listado de Etiquetas con sus Frecuencias
        int aa=0;
        for(a=0; a<NameLabels.size(); a++)
        {
            out[aa] = Float.parseFloat(NameLabels.get(a).toString());
            aa+=2;
        }        
        return out;
    }

    //--------------------------------------------------
    /**
     * Remueve las regiones dejando unicamente la más grande
     * @param dst Array de destino
     * @param _labels Listado de Etiquetas
     * @param _W Ancho de la imagen
     * @param _H Alto de la imagen
     */
    public static void RemoveRegions(float[] dst, float[] _labels, int _W, int _H)
    {
        int a,k;
        float imax = 0;
        float max=0.0f;

        if(_labels.length != 0)
        {
            for(a=0; a<dst.length; a++)
            {
                for(k=0; k<_labels.length; k+=2)
                {
                    if(dst[a] == _labels[k])
                    {
                        _labels[k+1] +=1;

                        if(_labels[k+1] > max)
                        {
                            max = _labels[k+1];
                            imax = _labels[k];
                        }
                    }
                }
            }
            for(a=0; a<dst.length; a++)
            {
                if(dst[a]!=imax)
                    dst[a] = 0.0f;
                else
                    dst[a] = 255.0f;
            }
        }
    }

    public static void NormalizarDimension(double[][] P_obs)
    {
        double xMax[]= new double[P_obs[0].length];//Double.MIN_VALUE;
        double xMin[]= new double[P_obs[0].length];//Double.MAX_VALUE;
        double K[] = new double[P_obs[0].length];;
        double P[] = new double[P_obs[0].length];;
        double nMin = 0;
        double nMax = 1;

        //xMax =
        Arrays.fill(xMax, Double.MIN_VALUE);
        Arrays.fill(xMin, Double.MAX_VALUE);

        

        for(int i=0; i<P_obs.length; i++)
        {
            for(int j=0; j<P_obs[i].length; j++)
            {
                xMax[j] = P_obs[i][j]>xMax[j] ? P_obs[i][j] : xMax[j];

                xMin[j] = P_obs[i][j]<xMin[j] ? P_obs[i][j] : xMin[j];
            }
        }

        //K = (nMin - nMax)/(xMin - xMax);
        //P = ((nMax * xMin)-(nMin * xMax))/(xMin - xMax);

        for(int i=0; i<K.length; i++)
        {
            K[i] = (nMin - nMax)/(xMin[i] - xMax[i]);
            P[i] = ((nMax * xMin[i])-(nMin * xMax[i]))/(xMin[i] - xMax[i]);
        }
        for(int i=0; i<P_obs.length; i++)
        {
            for(int j=0; j<P_obs[i].length; j++)
            {
                P_obs[i][j] = (K[j] * P_obs[i][j]) + P[j];
            }
        }        
    }

    //--------------------------------------------------
    /**
     * Enlista los directorios y archivos de los videos
     * @param path Dirección de los archivos
     * @param dirs Lista donde se almacenan las direcciones
     * @param fileType Tipo de archivo que se enlista
     */
    public static void ListDir(String path, ArrayList<String> dirs, String fileType)
    {
        String t = path;
        File f = new File(t);
        if(f.isDirectory())
        {
            String[] fn = f.list();
            for(int i=0; i< fn.length; i++)
            {
                t = path+"/"+fn[i];
                ListDir(t, dirs,fileType);
            }
        }
        else if(f.isFile())
        {
            if(path.substring(path.length()-fileType.length(), path.length()).equals(fileType))
                dirs.add(path);
        }
    }
}
//--------------------------------------------------
//FIN
//--------------------------------------------------